package openglsuperbible.Chapter4;



import openglsuperbible.glutils.*;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.*;
import static org.lwjgl.opengl.GL11.*;




public class SphereWorld4 {
        public static final int DISPLAY_HEIGHT = 600;
        public static final int DISPLAY_WIDTH = 800;
        private static final int NUM_SPHERES = 50;
        private GLFrame[] spheres;
        
        private GLShader shaderFLAT; 
        private GLShader shaderPOINT_LIGHT;
        // Shader
        
        private MatrixStack modelViewMatrix;  // Modelview Matrix
        private MatrixStack projectionMatrix; // Projection Matrix
        private GLFrustrum frustrum;    // View Frustum
        private GeometryTransform transformPipeline; // Geometry Transform Pipeline

        private GLBatch torusBatch;
        private GLBatch floorBatch;
        private GLBatch sphereBatch;
        private GLFrame cameraFrame;
        
        
        float vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};
        float vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
        float vSphereColor[] = { 0.0f, 0.0f, 1.0f, 1.0f };
        /** time at last frame */
	long lastFrame;
        
       public void initGL() {  
            shaderFLAT = GLShaderFactory.getFlatShader();
            
            shaderPOINT_LIGHT = GLShaderFactory.getPointLightDiffuseShader();

            glEnable(GL_DEPTH_TEST);

            glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

            frustrum = new GLFrustrum();               
            modelViewMatrix = new MatrixStack();
            cameraFrame = new GLFrame();
            // This makes a torus
            torusBatch = GLBatchFactory.makeTorus(0.4f, 0.15f, 30, 30);
            sphereBatch = GLBatchFactory.makeSphere(0.1f, 26, 13);  
            floorBatch = GLBatchFactory.makeGrid(40);
                                   
            spheres = new GLFrame[NUM_SPHERES];
    //                
    //      // Randomly place the spheres
            for(int i = 0; i < NUM_SPHERES; i++) {
                    float x = ((float)((Math.random() * 400) - 200) * 0.1f);
                    float z = ((float)((Math.random() * 400) - 200) * 0.1f);
                    float y = ((float)((Math.random() * 20)) * 0.1f);
                    spheres[i] = new GLFrame();                    
                    spheres[i].setOrigin(x, y, z); 
                }
         }   
  
            

        public void resizeGL() {   
            glViewport(0,0,DISPLAY_WIDTH ,DISPLAY_HEIGHT);
            frustrum.setPerspective(50f, Display.getWidth()/ Display.getHeight(),1.0f, 100.0f);
            projectionMatrix = new MatrixStack(frustrum.getProjectionMatrix());
            // Set the transformation pipeline to use the two matrix stacks 
            transformPipeline = new GeometryTransform(modelViewMatrix, projectionMatrix);
        } 
            // Respond to arrow keys by moving the camera frame of reference
        public void pollInput()
        {
            int delta = getDelta();
            float linear = 0.1f;
            float angular = (float)Math.toRadians(1.0f);

            if (Keyboard.isKeyDown(Keyboard.KEY_UP)) {
                    cameraFrame.moveForward(linear);
                    System.out.println("Key up");
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_DOWN)){
                    cameraFrame.moveForward(-linear);
                    System.out.println("Key Down");
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_LEFT)){
                    cameraFrame.rotateWorld(angular,0,1,0);

            }
            if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT)){
                    cameraFrame.rotateWorld(-angular,0,1,0);

            }
        }
            		
            
                        
        float angle = 0.0f;

        public void render() {
            angle+= 0.05f * getDelta();

            glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
            modelViewMatrix.push();
            shaderFLAT.useShader();                  
            // Save the current modelview matrix (the identity matrix)
            
            modelViewMatrix.push();
            float[] mCamera = cameraFrame.getCameraMatrix();
            modelViewMatrix.multiply4(mCamera);
            

                // Transform the light position into eye coordinates
             float[] vLightPos = { 0.0f, 10.0f, 5.0f, 1.0f };
             float[] vLightEyePos = Math3D.transformVector4( vLightPos, mCamera);
             
            shaderFLAT.setUniform4("vColor",vFloorColor); 

            shaderFLAT.setUniformMatrix4("mvpMatrix", false, transformPipeline.getModelViewProjectionMatrix()); 
            floorBatch.draw(shaderFLAT.getAttributeLocations()); 
            
            shaderPOINT_LIGHT.useShader();
 
                for(int i = 0; i < NUM_SPHERES; i++) 
                {
                    modelViewMatrix.push();
                    modelViewMatrix.multiply4(spheres[i].getMatrixf());
                    shaderPOINT_LIGHT.setUniform3("vLightPos",vLightEyePos);
                    shaderPOINT_LIGHT.setUniform4("vColor", vSphereColor);
                    shaderPOINT_LIGHT.setUniformMatrix4("pMatrix", false, transformPipeline.getProjectionMatrix());
                    shaderPOINT_LIGHT.setUniformMatrix4("mvMatrix", false, transformPipeline.getModelViewMatrix());
                    sphereBatch.draw(shaderPOINT_LIGHT.getAttributeLocations());
                    modelViewMatrix.pop();
                }

            modelViewMatrix.translate(0, 0, -2.5f);

            modelViewMatrix.rotate(angle, 0.0f, 1.0f, 0.0f);
            shaderPOINT_LIGHT.setUniform3("vLightPos",vLightEyePos);
            shaderPOINT_LIGHT.setUniform4("vColor", vTorusColor);
            shaderPOINT_LIGHT.setUniformMatrix4("pMatrix", false, transformPipeline.getProjectionMatrix());
            shaderPOINT_LIGHT.setUniformMatrix4("mvMatrix", false, transformPipeline.getModelViewMatrix());
            
            torusBatch.draw(shaderPOINT_LIGHT.getAttributeLocations());

            // Apply another rotation, followed by a translation, then draw the sphere
            modelViewMatrix.rotate(angle * -2.0f, 0.0f, 1.0f, 0.0f); 
            modelViewMatrix.translate(0.8f, 0.0f, 0.0f);
            shaderPOINT_LIGHT.setUniform3("vLightPos",vLightEyePos);
            shaderPOINT_LIGHT.setUniform4("vColor", vSphereColor);
            shaderPOINT_LIGHT.setUniformMatrix4("pMatrix", false, transformPipeline.getProjectionMatrix());
            shaderPOINT_LIGHT.setUniformMatrix4("mvMatrix", false, transformPipeline.getModelViewMatrix());

            sphereBatch.draw(shaderPOINT_LIGHT.getAttributeLocations());
 
            
            
//            // Restore the previous modleview matrix (the identity matrix)
            modelViewMatrix.pop();
            modelViewMatrix.pop();

            Display.update();  
        }  
            
        
        
       public void create() throws LWJGLException { 
            //Display    
            Display.setDisplayModeAndFullscreen(new DisplayMode(DISPLAY_WIDTH,DISPLAY_HEIGHT)); 
            
            Display.setTitle("SphereWorld 4");
            Display.create();
            
            //Keyboard         
            Keyboard.create();   
            //Mouse          
            Mouse.setGrabbed(false);
            Mouse.create();  
            //OpenGL      
            initGL();    
            resizeGL();  
        }  
       
       
       
            public void destroy() {  
                //Methods already check if created before destroying.  
                Mouse.destroy();   
                Keyboard.destroy();
                // Display.destroy();     
            } 
        
            public static void main(String[] args) {
            SphereWorld4 main = null;
            try { 
                 main = new SphereWorld4(); 
                main.create(); 
                main.run();
            }          
            catch(Exception ex) {  
                 
            }          
            finally { 
                if(main != null) {  
                    main.destroy();
                }         
            }      
        } 
            
            public void run() 
            {
                while (!Display.isCloseRequested() && !Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {              
                    if (Display.isVisible()) 
                    { 
                       
                        render();
                    }
                    else 
                    {     
                        if (Display.isDirty()) {
                           
                            render();
                        }
                    }          
            //Display.update();  
                pollInput();
                Display.sync(60);
                Display.setVSyncEnabled(true); 
                }                
                try{       
                    Thread.sleep(100); 
                } 
                catch (InterruptedException ex) {  }
             
            }
                        
                            	/** 
	 * Calculate how many milliseconds have passed 
	 * since last frame.
	 * 
	 * @return milliseconds passed since last frame 
	 */
	public int getDelta() {
	    long time = getTime();
	    int delta = (int) (time - lastFrame);
	    lastFrame = time;
	 
	    return delta;
	}
	
	/**
	 * Get the accurate system time
	 * 
	 * @return The system time in milliseconds
	 */
	public long getTime() {
	    return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
}
